#!/usr/bin/python
# -*- coding: utf-8 -*-

import sys
import time
import wx
import wx.grid as gridlib
import os
import logging

from scapy.all import *
import threading

########################################################################

global myGrid
global textbox
global pkt

shutdown = False

CaptureOn = False

FileName = ""
TotalFrames = 0
FirstFrameTime = 0.0
TotalTime = 0.0
wildcard = "Ficheiro de captura(*.cap)|*.cap|" 
NBytes = 0      

########################################################################

class DumpPacket(wx.Dialog):
    def __init__(self, parent, id, title):
        wx.Dialog.__init__(self, parent, id, title, wx.DefaultPosition, wx.Size(300, 130))
 
        wx.StaticText(self, -1, 'Selecione o Pacote de que quer \nvisualizar o cabecalho\n\n1 - '+str(TotalFrames), (20,20))
 
        self.sc = wx.SpinCtrl(self, -1, '',  (20, 95), (80, -1))
        self.sc.SetRange(1, TotalFrames)
        self.sc.SetValue(1)
        self.sc.GetValue()

        cancel_btn = wx.Button(self, 1, 'Cancelar', (110, 93))
        clear_btn = wx.Button(self, 2, 'OK', (205, 93))
        clear_btn.SetFocus()
 
        self.Bind(wx.EVT_BUTTON, self.OnCompute, id=1)
        self.Bind(wx.EVT_BUTTON, self.OnClose, id=2)
        self.Bind(wx.EVT_CLOSE, self.OnClose)
 
    def OnCompute(self, event):
        self.Destroy()
 
    def OnClose(self, event):
        pkt[self.sc.GetValue()-1].pdfdump()
        #print str(self.sc.GetValue()-1)
        self.Destroy()

def arp_monitor_callback(pkt):
    global TotalFrames
    global FirstFrameTime
    global TotalTime
    global NBytes
    global myGrid
    if ARP in pkt and pkt[ARP].op in (1,2 ): #who-has or is-at
        a = pkt.sprintf("%ARP.hwsrc% %ARP.psrc% %ARP.hwdst% %ARP.pdst%\n\t %ARP.time%")
        view.Refresh(True)
        myGrid.Refresh(True)
        myGrid.Show(True)
        NBytes = NBytes + len(pkt)
        counter = TotalFrames
        TotalFrames +=1
        if TotalFrames == 1: FirstFrameTime = pkt.time
        TotalTime = pkt.time - FirstFrameTime
        myGrid.InsertRows(TotalFrames,1)
        myGrid.SetCellValue(TotalFrames-1, 0, str(TotalTime))
        myGrid.SetCellValue(TotalFrames-1, 1, str(pkt.hwdst))
        myGrid.SetCellValue(TotalFrames-1, 2, str(pkt.pdst))
        myGrid.SetCellValue(TotalFrames-1, 3, str(pkt.summary()))
        #print TotalFrames,")",TotalTime, pkt.summary()
        pass
    #print ("\n")

########################################################################

class ScapyThread(threading.Thread):
    def run(self):
	    global textbox
        global CaptureOn
        global TotalFrames
        global FileName
        global NBytes
        NBytes = 0
        FileName = ""
        TotalFrames = 0
	    textbox.SetValue("*** Sniffer de ARP iniciado ***")
        print "*** Sniffer de ARP iniciado ***"
        global pkt
        pkt = sniff(prn=arp_monitor_callback, filter="arp", store=1, stop_filter=self.stoper)
        CaptureOn = True
        wrpcap("temp.cap",pkt)
        print "*** Sniffer de ARP parou ***\n"
        if TotalFrames > 0:
            CaptureOn = True
        textbox.SetValue("*** Sniffer de ARP parou ***")
        #print ("\n")
        #print "frames:", TotalFrames, "bytes: ", NBytes, "Tempo Total: ",TotalTime , " segundos"

    
 
    def stoper(self, stop):
        global shutdown
        return shutdown


########################################################################

def FramesTreat():
    x = 0
    global TotalFrames
    global FirstFrameTime
    global TotalTime
    global NBytes
    myGrid.DeleteRows(0, TotalFrames)
#    TotalFrames = len(pkt)
    TotalFrames = 0
    NBytes = 0

    for x in range(0,len(pkt)):
        NBytes = NBytes + len(pkt[x])
        arp_monitor_callback(pkt[x])

    FirstFrameTime = pkt[0].time
    TotalTime = pkt[TotalFrames-1].time - FirstFrameTime

    #print TotalFrames, NBytes

########################################################################

def SummaryBox():
    temp_1 = time.strftime("%d/%m/%Y %H:%M:%S.%%06i",
                     time.localtime(FirstFrameTime)) % int((FirstFrameTime-int(FirstFrameTime))*1000000)

    temp_f = time.strftime("%d/%m/%Y %H:%M:%S.%%06i",
                     time.localtime(FirstFrameTime+TotalTime)) % int((FirstFrameTime+TotalTime-int(FirstFrameTime+TotalTime))*1000000)

    temp_t = time.strftime("%M:%S.%%06i",
                     time.localtime(TotalTime)) % int((TotalTime-int(TotalTime))*1000000)

    avg_pkt_s = str(TotalFrames / TotalTime)

    texto_ =  "\n" 
    if len(FileName) > 0:
        texto_ = (texto_ + "FICHEIRO\n " )
        texto_ = (texto_ + "\tNome:\t\t"  + FileName) 
        texto_ = (texto_ +  "\n\tTamanho:\t" + str(os.path.getsize(FileName)) + " bytes" )
        pass
    texto_ = (texto_ + "\n\nTEMPO\n")
    texto_ = (texto_ + "\tPrimeiro Pacote:\t" + temp_1 + "\t\t")
    texto_ = (texto_ + "\n\tUltimo Pacote:\t" + temp_f)
    texto_ = (texto_ + "\n\tTempo decorrido:\t" + temp_t)
    texto_ = (texto_ + "\n\nTRAFEGO\n")
    texto_ = (texto_ + "\tPacotes:\t\t\t" + str(TotalFrames))
    texto_ = (texto_ + "\n\tTempo Decorrido:\t" + str(TotalTime) +" segundos\t\t")
    texto_ = (texto_ + "\n\tPacotes/Segundo:\t" + avg_pkt_s )
    texto_ = (texto_ + "\n\tMed Tam Pacote:\t" + str(NBytes/TotalFrames) + " bytes" )
    texto_ = (texto_ + "\n\tBytes:\t\t\t" + str(NBytes) )
    texto_ = (texto_ + "\n\tBytes/Segundo:\t" + str(NBytes/TotalTime) + " bytes" )
    dlg = wx.MessageBox(texto_, 'FaroArp: Sumário',wx.OK | wx.ICON_INFORMATION)

########################################################################

def ResumeBox():
    gratuits = 0
    requests = 0
    replys = 0
    arpping = 0

    for x in range(0,TotalFrames):
        if pkt[x].sprintf("%ARP.hwsrc%") == "00:00:00:00:00:00":
            gratuits += 1
        if pkt[x].op == 1:
            requests += 1
            if pkt[x].sprintf("%ARP.hwdst%") == "00:00:00:00:00:00":
                if pkt[x].sprintf("%ARP.hwsrc%") != "00:00:00:00:00:00":
                    arpping +=1
        if pkt[x].op == 2:
            replys += 1

    texto_ =  "\n" 
    texto_ = (texto_ + "Pacotes ARP\t\t\t\t\t\t\n " )
    texto_ = (texto_ + "\n\tPacotes:\t\t" + str(TotalFrames))
    texto_ = (texto_ + "\n\n\tGratuitos:\t"  + str(gratuits) + " (" + str(gratuits*100/TotalFrames) + "%)") 
    texto_ = (texto_ + "\n\n\tPedidos:\t\t" + str(requests)  + " (" + str(requests*100/TotalFrames) + "%)")
    texto_ = (texto_ + "\n\n\tRespostas:\t" + str(replys)  + " (" + str(replys*100/TotalFrames) + "%)")
    dlg = wx.MessageBox(texto_, 'FaroArp: Estatisticas sobre ARP',wx.OK | wx.ICON_INFORMATION)

########################################################################
class MainWindow(wx.Frame):
    def __init__(self,parent,title):
        # based on a frame, so set up the frame
        wx.Frame.__init__(self,None, wx.ID_ANY, title=title, size=(800,500))

        # Center the main frame on screen        
        self.Centre()

        # Add a text editor and a status bar
        self.CreateStatusBar() # A Statusbar in the bottom of the window
        self.SetStatusText('Bem Vindos ao FaroArp')

        # Creating the menubar.
        menuBar = wx.MenuBar()

        self.control = wx.TextCtrl(self, 1, style=wx.TE_MULTILINE)

        # Creating two panels
        panel1 = wx.Panel(self,-1, size=(200,35))
        self.panel2 = wx.Panel(self,-1)

        panel1.SetBackgroundColour("CYAN")
        self.panel2.SetBackgroundColour("GRAY")

        box = wx.BoxSizer(wx.VERTICAL)
        box.Add(panel1, 0, wx.EXPAND)
        box.Add(self.panel2, 8, wx.EXPAND)
 
        self.SetAutoLayout(False)
        self.SetSizer(box)
        self.Layout()

        # FILE MENU ########
        #
        # Setting up the menu. filemenu is a local variable at this stage.
        filemenu=wx.Menu()

        # use ID_ for future easy reference - much better that "48", "404" etc
        # The & character indicates the short cut key
        self.open_ = filemenu.Append(wx.ID_OPEN, "&Abrir"," Abrir um ficheiro de captura")
        filemenu.AppendSeparator()
        self.save_ = filemenu.Append(wx.ID_SAVE, "&Guardar"," Guardar o resultado da captura em ficheiro")
        filemenu.AppendSeparator()
        exit_ = filemenu.Append(wx.ID_EXIT,"&Sair"," Terminar o programa")

        # Define the code to be run when a menu from filemenu option is selected
        self.Bind(wx.EVT_MENU, self.OnOpen, self.open_)
        self.Bind(wx.EVT_MENU, self.OnSave, self.save_)
        self.Bind(wx.EVT_MENU, self.OnExit, exit_)

        menuBar.Append(filemenu,"&Ficheiro") # Adding the "filemenu" to the MenuBar
        # STATISTICS MENU ########
        #
        # Setting up the menu. statistic is a local variable at this stage.
        statistics = wx.Menu()

        menuBar.Append(statistics,"&Estatisticas") # Adding the "statistic" to the MenuBar
        self.summary_=statistics.Append(wx.NewId(),"S&umario","Resumo sobre os pacotes capturados")
        self.plot_=statistics.Append(wx.NewId(),"&Cabeçalho","Header de um pacote")
        self.resume_=statistics.Append(wx.NewId(),"&Resumo Arp","Dados Estatiscos dos ARP")

        self.Bind(wx.EVT_MENU, self.OnSummary, self.summary_)
        self.Bind(wx.EVT_MENU, self.OnPlot, self.plot_)
        self.Bind(wx.EVT_MENU, self.OnResume, self.resume_)
        # HELP MENU ########
        #
        # Setting up the menu. help is a local variable at this stage.
        help = wx.Menu() 

        help_ = help.Append(wx.ID_HELP,"Ajuda","Ajudas sobre o Programa")
        madeby = help.Append(wx.ID_ABOUT,"Acerca(Made By)","Feito por...")

        # Define the code to be run when a menu from filemenu option is selected
        self.Bind(wx.EVT_MENU, self.OnHelp, help_)
        self.Bind(wx.EVT_MENU, self.OnAbout, madeby)

        menuBar.Append(help,"A&juda") # Adding the "help" to the MenuBar

        # SET MENU BAR ########
        self.SetMenuBar(menuBar) # Adding the MenuBar to the Frame content.

        # Note - previous line stores the whole of the menu into the current object


        #----------------- Botoes---------------------------------------------
        self.button1 = wx.Button(panel1, label='Iniciar',pos=(5,2))
        self.button1.Bind(wx.EVT_BUTTON, self.SniffStart)
       
        self.button2 = wx.Button(panel1, label='Parar',pos=(120,2))
        self.button2.Disable()
        self.button2.Bind(wx.EVT_BUTTON, self.SniffStop)
       
        exit_button = wx.Button(panel1, label='Sair',pos=(688,2))
        self.Bind(wx.EVT_BUTTON, self.closebutton, exit_button)            # set the event atribute
        self.Bind(wx.EVT_CLOSE, self.OnExit)                               # set the event for exit the program

        #----------------Fim botoes-------------------------------------------

        global textbox
        textbox= wx.TextCtrl(panel1, pos=(235,2), size=(445,30),style=wx.TE_CENTER)

        # GRELHA ########

        #Grelha inicializacao-------------------
        global myGrid
        myGrid = gridlib.Grid(self.panel2)
        myGrid.CreateGrid(0, 4)
        myGrid.SetColLabelValue(0, "Tempo")
        myGrid.SetColLabelValue(1, "Mac Source")
        myGrid.SetColLabelValue(2,"IP Source")
        myGrid.SetColLabelValue(3,"Informaçao")
        myGrid.SetRowLabelSize(wx.grid.GRID_AUTOSIZE)
        myGrid.SetColLabelSize(wx.grid.GRID_AUTOSIZE)
     
        myGrid.SetColSize(0,130)
        myGrid.SetColSize(1,170)
        myGrid.SetColSize(2,145)
        myGrid.SetColSize(3,300)

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(myGrid,1, wx.EXPAND)
        self.panel2.SetSizer(sizer)
        self.panel2.Refresh(True)
        myGrid.Refresh(True)

        # SHOW MAIN WINDOW ########
        # Show it !!!
        self.MenusOnStart(False)
        self.MenuSaveOn(False)
        self.MenuOpenOn(True)
        self.Show(True)

        # dirname is an APPLICATION variable that we're choosing to store
        # in with the frame - it's the parent directory for any file we
        # choose to edit in this frame
        self.dirname = ''

    ### DEFS MainWindow
    ### FILE MENU

    def OnOpen(self,e):
        # In this case, the dialog is created within the method because
        # the directory name, etc, may be changed during the running of the
        # application. In theory, you could create one earlier, store it in
        # your frame object and change it when it was called to reflect
        # current parameters / values
        global CaptureOn
        ContinueOn = True
        if CaptureOn:
            msgCaptureSave = wx.MessageDialog(self,'Tem uma captura para gravar\nPretende encerrar sem gravar?','Tem certeza',wx.YES_NO| wx.ICON_QUESTION)
            if msgCaptureSave.ShowModal() == wx.ID_YES:
                msgCaptureSave.Destroy()
                ContinueOn = True
            else:
                ContinueOn = False
        if ContinueOn:
            dlg = wx.FileDialog(self, "Abrir um ficheiro de captura", self.dirname, "", "*.cap", wx.OPEN)
            if dlg.ShowModal() == wx.ID_OK:
                global FileName
                self.dirname=dlg.GetDirectory()
                self.filename = dlg.GetFilename()
                FileName  = os.path.join(self.dirname, self.filename)
                global pkt
                pkt = rdpcap(FileName)
                FramesTreat()
                CaptureOn = False
            dlg.Destroy()

    def OnSave(self,e):
        # Save away the edited text
        # Open the file, do an RU sure check for an overwrite!
        dlg = wx.FileDialog(self, "Gravar captura em ficheiro", self.dirname, "", "*.cap", \
                wx.SAVE | wx.OVERWRITE_PROMPT)
        if dlg.ShowModal() == wx.ID_OK:
            # Grab the content to be saved
            itcontains = self.control.GetValue()
            # Open the file for write, write, close
            global FileName
            self.filename = dlg.GetFilename()
            self.dirname=dlg.GetDirectory()
            FileName  = os.path.join(self.dirname, self.filename)
            global pkt
            wrpcap(FileName,pkt)
            global CaptureOn
            CaptureOn = False
        dlg.Destroy()

    def closebutton(self, event):
        self.Close(True)

    def OnExit(self,e):
        ExitOn = True
        if CaptureOn:
            msgCaptureSave = wx.MessageDialog(self,'Tem uma captura para gravar\nPretende encerrar sem gravar?','Tem certeza',wx.YES_NO| wx.ICON_QUESTION)
            if msgCaptureSave.ShowModal() == wx.ID_YES:
                msgCaptureSave.Destroy()
                ExitOn = True
            else:
                ExitOn = False
        if ExitOn:
            msgdialogo = wx.MessageDialog(self,'Fechar Programa','Tem certeza',wx.YES_NO| wx.ICON_QUESTION)
            if msgdialogo.ShowModal() == wx.ID_YES:
                msgdialogo.Destroy()
                self.Destroy()
            else:
                msgdialogo.Destroy()
            

### STATISTICS MENU
  #
    def OnSummary(self,e):
       SummaryBox()

    def OnPlot(self,e):
        dlg = DumpPacket(None, -1, 'Cabeçalho')
        dlg.Show(True)
        dlg.Centre()

    def OnResume(self, e):
        ResumeBox()

### HELP MENU
  #
    def OnHelp(self,e):
        dlg = wx.MessageBox('Menu Ficheiro:\t\n Nova\n-Cria uma nova captura\t\n Abrir\n-Abre uma captura com extensao .cap\t\n Gravar\t\n-Gravar uma captura com extensao .cap\t\n Sair\t\n-Encerra o programa perguntando ao utilizador se realmente quer sair','Ajuda Simples',wx.OK | wx.ICON_INFORMATION)

    def OnAbout(self,e):
        dlg = wx.MessageBox('Trabalho realizado por:\n Luis Reis Nº12829\t\n Helder Tomás Nº13338\t\n Paulo Viegas Nº12834\t\n\n IPBEJA ESTIG\t\n Versao 1.0 - MESI (2013)', 'Autores', wx.OK | wx.ICON_INFORMATION)



### BOTOES
  #
    def SniffStart(self, e):
        
        self.button1.Disable()      
        self.button2.Enable()
        myGrid.DeleteRows(0, TotalFrames)
        global shutdown
        shutdown = False
        self.MenuOpenOn(False)
        self.MenusOnStart(False)
        ScapyThread().start()

    def SniffStop(self, e):
        self.MenuSaveOn(True)
        self.MenuOpenOn(True)
        self.MenusOnStart(True)
        self.button1.Enable()      
        self.button2.Disable()
        global shutdown
        shutdown = True
        self.Refresh()
  
    def MenusOnStart(self, P):
        self.summary_.Enable(P)
        self.plot_.Enable(P)
        self.resume_.Enable(P)

    def TotalMenus(self):
        self.summary_.Enable(True)
        self.plot_.Enable(True)
        self.resume_.Enable(True)

    def MenuSaveOn(self, P):
        self.save_.Enable(P)

    def MenuOpenOn(self, P):
        self.open_.Enable(P)


# Set up a window based app, and create a main window in it
app = wx.App()
st = ScapyThread()
view = MainWindow(None, "Programa para Captura de Pacotes ARP")
# Enter event loop
app.MainLoop()
